home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / What's New? / Development Kits / Mac OS / USB DDK 1.4.6f4 / Examples / USBSampleStorageDriver / UnitTableDriver / UnitTableDeviceAccess.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-25  |  60.6 KB  |  2,162 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        UnitTableDeviceAccess.c
  3.  
  4.     Contains:    All USB interface device access specific functions
  5.  
  6.     Version:    1.0
  7.  
  8.     Copyright:    © 1998-2000 by Apple Computer, Inc., all rights reserved.
  9.  
  10.  
  11. */
  12.  
  13. #include "UnitTableDeviceAccess.h"
  14.  
  15. #include <DriverGestalt.h>
  16. #include <DriverServices.h>
  17. #include <USB.h>
  18.  
  19. #include "USB_StdCommands.h"
  20. #include "StorageDeviceConfiguration.h"
  21. #include "USB_Version.h"
  22. #include "USB_ClassDriverAccess.h"
  23. #include "USB_ManualEjectSupport.h"
  24. #include "USB_ShimServicesSupport.h"
  25. #include "StorageClassPublicAPI.h"
  26.  
  27. #pragma mark -
  28. #pragma mark Private Module Global Variables
  29. // USB specific module data
  30. static USBDeviceRef                                    gDeviceUSBRefNum;                // The device's USB Device Ref number
  31.  
  32. // This is used by other modules -- needs a good home
  33. UInt8                                                 gDeviceUSBSubClass;
  34.  
  35. // Interface independent module data
  36. static RegEntryIDPtr                                gDeviceRegEntryIDPtr;
  37. static DriverRefNum                                    gDrvrRefNum;
  38. static Str32                                        gDeviceWhereString;
  39. static SupportedMediaStruct                            gSupportedMediaTypes = { {0}, { 'aaaa','aaaa','aaaa','aaaa'}};
  40. static ReadCapacityData                                gCurrentMediaCapacity = {0,0};
  41. static Boolean                                        gMediaIsWriteProtected = false;
  42. static Boolean                                        gPreventCommandFailed = false;
  43. static UInt8                                        gCommandSetANSIVersion = 0;
  44. static UInt8                                        gPeripheralDeviceType = 0;
  45.  
  46. // This flag indicates whether an eject sequence is in progress.  This allows us
  47. // to repsond immendiately to an eject call and not force the client to have to wait
  48. // for the ejection to complete.
  49. static Boolean                                        gWaitingForEjectCompletion = false;
  50.  
  51. // Is the controlled device a fixed disk ( hard disk ) drive
  52. static Boolean                                gDriveIsFixed = false;
  53. static Boolean                                gRemountFixed = true;
  54.  
  55. // Does the controlled device support floppy media?
  56. static Boolean                                gDriveSupportsFloppies = false;
  57.  
  58. // Does the controlled device support non-floppy removable media (i.e. Zip, LS-120, Orb etc)?
  59. static Boolean                                gDriveSupportsNonFloppyRemovable = false;
  60.  
  61. static DriverGestaltDeviceModelInfoResponse    gDeviceInfoStruct;
  62.  
  63. // Device Info strings
  64. static Str63                            gVendorIDString;
  65. static Str63                            gProductIDString;
  66. static Str63                            gFWRevisionString;
  67. static Str63                            gFWSubRevisionString;
  68. static Str63                            gSerialNumberString;
  69.  
  70. #pragma mark -
  71. #pragma mark Private Module Routines
  72. static OSStatus     InitializeDriveAccess( IntDriveRequestPBPtr requestPB, void *dispatchPtr );
  73. static OSStatus     TerminateDriveAccess ( void );
  74.  
  75. static void         ExternalRequestComplete ( IntDriveRequestPBPtr requestPB );
  76.  
  77. // Completion routines for Read/Write requests.
  78. static void ReadWriteRequestComplete ( IntDriveRequestPBPtr requestPB );
  79. static void WriteRequestSenseComplete ( IntDriveRequestPBPtr requestPB );
  80. static void ReadWriteRequestSenseOnErrorComplete ( IntDriveRequestPBPtr requestPB );
  81.  
  82. #pragma mark -
  83. #pragma mark External Query Functions
  84. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  85. // External Query Functions
  86. // These are all functions available externally for querying 
  87. // specific information about the device and interface
  88. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  89. Boolean IsDeviceAccessEnabled( void )
  90. {
  91.     if ( IsClassDispatchTableValid() == false )
  92.     {
  93.         return false;
  94.     }
  95.     else
  96.     {
  97.         Boolean     result;
  98.         OSStatus     status;
  99.         UInt32         classDriverStatus;
  100.         
  101.         status = SendClassStatusCall( kUSBStorageStatusGetServicesStatus, &classDriverStatus );
  102.     
  103.         if ( (classDriverStatus == kUSBStorageServicesConfigureComplete) && ( status == noErr ) )
  104.         {
  105.             result = true;
  106.         }
  107.         else
  108.         {
  109.             result = false;
  110.         }
  111.  
  112.         return result;
  113.     }
  114. }
  115.  
  116. Boolean    DoesInterfaceHaveVirtualMemoryCapabilities( void )
  117. {
  118.     // Since USB needs software interrupt support, a USB device
  119.     // can never be in the VM page map path and so the DAM will
  120.     // always return false.
  121.     return false;
  122. }
  123.  
  124. // Accessor for the Pascal String of the InterfaceType
  125. StringPtr GetDeviceWhereString( void )
  126. {
  127.     return gDeviceWhereString;
  128. }
  129.  
  130. void GetMediaProperties ( UInt32 *TotalBlocksOnMedia, UInt32 *BlockLengthInBytes, Boolean *IsWriteProtected, OSType *currentType )
  131. {
  132.     *TotalBlocksOnMedia = gCurrentMediaCapacity.TotalBlocksOnMedia;            // Total number of blocks
  133.     *BlockLengthInBytes = gCurrentMediaCapacity.BlockLengthInBytes;            // Total number of blocks
  134.     *IsWriteProtected = gMediaIsWriteProtected;
  135.  
  136.     if ( gDriveIsFixed == true )
  137.     {
  138.         *currentType = kdgDiskType;
  139.     }
  140.     else if ( gPeripheralDeviceType == kInqCDROM_MMCDevice )
  141.     {
  142.         *currentType = kdgCDType;
  143.     }
  144.     else
  145.     {
  146.         // This is a removable disk, set the default media type
  147.         *currentType = kdgRemovableType;
  148.         if ( gDriveSupportsFloppies == true )
  149.         {
  150.             // If device supports floppies, and this is one
  151.             // return the correct media type
  152.             if(    gCurrentMediaCapacity.TotalBlocksOnMedia <= 0x0C00L)
  153.             {
  154.                 *currentType = kdgFloppyType;
  155.             }
  156.         }
  157.     }
  158. }
  159.  
  160. // Accessor for Device Ref number
  161. UInt32 GetDeviceReferenceNumber( void )
  162. {
  163.     return ((UInt32) gDeviceUSBRefNum);
  164. }
  165.  
  166. // Accessor for the DriverGestalt InterfaceType
  167. OSType GetDriverGestaltIntfForInterface( void )
  168. {
  169.     return kdgUSBIntf;
  170. }
  171.  
  172. // Accessor for the DeviceInfo struct
  173. DriverGestaltDeviceModelInfoResponse *GetDeviceInfoPtr( void )
  174. {
  175.     return &gDeviceInfoStruct;
  176. }
  177.  
  178. void GetDriverVersionNumber( NumVersion *theVersion )
  179. {
  180.     theVersion->majorRev         = kStorageHexMajorVers;
  181.     theVersion->minorAndBugRev     = kStorageHexMinorVers;
  182.     theVersion->stage             = kStorageReleaseStage;
  183.     theVersion->nonRelRev         = kStorageCurrentRelease;
  184. }
  185.  
  186. // Accessor for the Supported Media types struct
  187. DriverGestaltSupportedMediaTypesResponse *GetSupportedMediaTypesPtr( void )
  188. {
  189.     return (DriverGestaltSupportedMediaTypesResponse *) &gSupportedMediaTypes;
  190. }
  191.  
  192. #pragma mark -
  193. #pragma mark Initialize/Terminate DAM Functions
  194. // Setup the access to the drive if the dispatchPtr is nil just return
  195. // and wait for this function to be called with a valid dispatchPtr.
  196. // -- For Interfaces that don't need a dispatch table setup will initialize
  197. // the Device access regardless of what is passed for dispatchPtr
  198. OSStatus InitializeDeviceAccess(  DriverRefNum theRefNum, RegEntryIDPtr theRegEntryPtr  )
  199. {
  200.     Str32 tempStr;
  201.     
  202.     gDrvrRefNum = theRefNum;
  203.     gDeviceRegEntryIDPtr = theRegEntryPtr;
  204.     
  205.     gDeviceInfoStruct.infoStructVersion = kInfoStructStringPtrsVers1;
  206.     gDeviceInfoStruct.vendorName = nil;
  207.     gDeviceInfoStruct.productName = nil;
  208.     gDeviceInfoStruct.revisionNumber = nil;
  209.     gDeviceInfoStruct.subRevisionNumber = nil;
  210.     gDeviceInfoStruct.serialNumber = nil;
  211.  
  212.     // Build the Drive Info string returned in Status/Control calls
  213.     PStrCopy( gDeviceWhereString, "\pUSB (v");
  214.     CStrToPStr(tempStr, kStorageStringVersShort);     // driver version
  215.     PStrCat( gDeviceWhereString, tempStr);             // driver version
  216.     PStrCat( gDeviceWhereString, "\p)");
  217.     
  218.     return noErr;
  219. }
  220.  
  221. OSStatus TerminateDeviceAccess( DriverRefNum theRefNum, RegEntryIDPtr theRegEntryPtr )
  222. {
  223. #pragma unused ( theRefNum, theRegEntryPtr )
  224.  
  225.     return TerminateDriveAccess();    
  226. }
  227.  
  228. #pragma mark -
  229. #pragma mark Control and Status
  230. OSStatus HandleControlRequest ( UInt32 userData, CntrlParamPtr cntrlPBPtr, ControlStatusCompletionProcPtr callBack )
  231. {
  232.     OSStatus                 err;
  233.     
  234.     switch(cntrlPBPtr->csCode) 
  235.     {
  236.         // This control call is made by the Shim to inform the driver of the dispatch table for
  237.         // its class driver.
  238.         case kInitializeDeviceAccess:
  239.         {
  240.             IntDriveRequestPBPtr     ourPB;
  241.             
  242.             ourPB = GetDriveInternalPB();
  243.             
  244.             ourPB->userData = userData;
  245.             ourPB->userCompletionProc = callBack;
  246.     
  247.             //SysDebugStr("\pInit Drive Access");
  248.             err = InitializeDriveAccess( ourPB, (void *) *((UInt32 *)(&cntrlPBPtr->csParam[0])));
  249.             if ( err != kRequestPending )
  250.             {
  251.                 //SysDebugStr("\pInit Drive Access Error occurred");
  252.                 FreeInternalPB( ourPB );
  253.                 if (( err != noErr ) && ( err != kClassNotConfiguredError ))
  254.                 {
  255.                     // An error occurred and it was not Class not configured
  256.                     // Terminate the driver and allow the class to be replaced.
  257.                     // We do not care about an errors from Terminate and will return
  258.                     // the error previously returned from Initialize.
  259.                     (void) TerminateDriveAccess();
  260.                 }
  261.             }
  262.         }
  263.         break;
  264.  
  265.         case kTerminateDeviceAccess:
  266.         {
  267.             // This control call is made by the Shim to inform the driver that the dispatch table is
  268.             // no longer valid.
  269.             err = TerminateDriveAccess();
  270.         }
  271.         break;
  272.         
  273.         default:
  274.         {
  275.             err = controlErr;
  276.         }
  277.     }
  278.  
  279.     return err;
  280. }
  281.  
  282. OSStatus    HandleStatusRequest ( UInt32 userData, CntrlParamPtr cntrlPBPtr, ControlStatusCompletionProcPtr callBack )
  283. {
  284. #pragma unused ( userData, callBack ) 
  285.     OSStatus            err            = noErr;
  286.  
  287.     // Handle the status call
  288.     switch(cntrlPBPtr->csCode)
  289.     {
  290.         default:
  291.         {
  292.             err = statusErr;
  293.         }
  294.         break;
  295.     }
  296.     
  297.     return err;
  298. }
  299.  
  300. #pragma mark -
  301. #pragma mark Initialize Drive Access 
  302.  
  303. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  304. // Initialize Drive Access 
  305. // These are all functions and definitions used for handling
  306. // the initialization of drive access
  307. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  308.  
  309. enum
  310. {
  311.     kInitStartState = 1,
  312.     kInitTURDoneState,
  313.     kInitReqSenseDoneState,
  314.     kInitGetInquirySizeDoneState,
  315.     kInitInquiryDoneState
  316. };
  317.  
  318. static void InitStateMachine( IntDriveRequestPBPtr ourPB );
  319.  
  320. OSStatus InitializeDriveAccess( IntDriveRequestPBPtr requestPB, void *dispatchPtr )
  321. {
  322.     USBStorageClassSetupTablePtr    theSetupTablePtr;
  323.  
  324.     if ( dispatchPtr == nil )
  325.     {
  326.         return noErr;
  327.     }
  328.     
  329.     theSetupTablePtr = (USBStorageClassSetupTablePtr) dispatchPtr;
  330.     
  331.     gDeviceUSBRefNum         = theSetupTablePtr->usbDeviceRef;
  332.     gDeviceUSBSubClass         = theSetupTablePtr->usbSubClass;
  333.     SetShimDispatchTable( gDrvrRefNum, theSetupTablePtr->shimDispatchTable );
  334.     SetClassDispatchTable( theSetupTablePtr->theStorageClassDispatchTable );
  335.  
  336.     gSupportedMediaTypes.supportTypesCount = 0;
  337.  
  338.     if( IsClassDispatchTableValid() == true )
  339.     {
  340.         // Initalize the Class Driver        
  341.         (void) InitializeClassAccess();
  342.     }
  343.     else
  344.     {
  345.         return kClassNotConfiguredError;
  346.     }
  347.  
  348.     if ( IsDeviceAccessEnabled() == true )
  349.     {
  350.         StringPtr    getClassStringPtr;
  351.         OSStatus    status;
  352.  
  353.         // Get the Vendor String from the class driver and copy it here
  354.         status = SendClassStatusCall(kUSBStorageStatusGetVendorStringPtr, &getClassStringPtr);
  355.         if( status == noErr )
  356.         {
  357.             if ( getClassStringPtr != nil )
  358.             {
  359.                 PStrCopy( gVendorIDString, getClassStringPtr);
  360.                 gDeviceInfoStruct.vendorName = gVendorIDString;
  361.     
  362.             }
  363.         }
  364.         
  365.         // Get the Product String from the class driver and copy it here    
  366.         status = SendClassStatusCall(kUSBStorageStatusGetProductStringPtr, &getClassStringPtr);
  367.         if( status == noErr )
  368.         {
  369.             if ( getClassStringPtr != nil )
  370.             {
  371.                 PStrCopy( gProductIDString, getClassStringPtr);
  372.                 gDeviceInfoStruct.productName = gProductIDString;
  373.     
  374.             }
  375.         }
  376.         
  377.         status = SendClassControlCall(kUSBStorageControlSetParentsRefNumber, &theSetupTablePtr->usbParentRef);
  378.         
  379.         requestPB->currentExecutionState = kInitStartState;
  380.         requestPB->completionProc = &InitStateMachine;
  381.         requestPB->status = noErr;
  382.         InitStateMachine( requestPB );
  383.     
  384.         return kRequestPending;
  385.     }
  386.  
  387.     return kClassNotConfiguredError;
  388. }
  389.  
  390. static OSStatus ProcessInquiryData( UInt8 *InquiryBuffer );
  391.  
  392. void InitStateMachine( IntDriveRequestPBPtr ourPB )
  393. {
  394.     OSStatus err = ourPB->status;
  395.  
  396.     switch( ourPB->currentExecutionState )
  397.     {
  398.         case kInitStartState:
  399.         {
  400.             // Let's do a TUR and Request sense to make sure the drive is in
  401.             // a good state
  402.             ourPB->currentExecutionState = kInitTURDoneState;
  403.             ourPB->completionProc = &InitStateMachine;
  404.             err = TestUnitReadyBuilder( ourPB );
  405.         }
  406.         break;
  407.     
  408.         case kInitTURDoneState:
  409.         {
  410.             ourPB->currentExecutionState = kInitReqSenseDoneState;
  411.             ourPB->completionProc = &InitStateMachine;
  412.             err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
  413.         }
  414.         break;
  415.         
  416.         case kInitReqSenseDoneState:
  417.         {
  418.             FixRequestSenseData( ourPB );
  419.             err = ourPB->status;
  420.             if ( err == noErr )
  421.             {
  422.                 UInt8 inquiryCount;
  423.                 
  424.                 ourPB->currentExecutionState = kInitGetInquirySizeDoneState;
  425.                 ourPB->completionProc = &InitStateMachine;
  426.                 inquiryCount = kDeviceMaxInquiryCount;
  427.  
  428.                 if (( inquiryCount > 6 ) || ( inquiryCount == 0 ))
  429.                 {
  430.                     // Ask the device for 6 bytes, because most devices will give
  431.                     // us 6 anyways ( this way we avoid an overrun error)
  432.                     inquiryCount = 6;
  433.                 }
  434.                 
  435.                 err = InquiryBuilder( ourPB, ourPB->buffer, inquiryCount );
  436.             }
  437.             else
  438.             {
  439.                 //err = kClassNotConfiguredError;
  440.                 // We could not get a request sense, device may not be responding
  441.                 // allow shim to remove this driver.
  442.                 err = ioErr;
  443.             }
  444.         }
  445.         break;
  446.         
  447.         case kInitGetInquirySizeDoneState:
  448.         {
  449.             if ( err == noErr)
  450.             {
  451.                 UInt8    TotalOfInq;
  452.                 
  453.                 TotalOfInq = 5 + ourPB->buffer[4];
  454.                 
  455.                 // Check to make sure we are not asking for more
  456.                 // data than the buffer can hold. 
  457.                 if ( TotalOfInq > kInternalPBBufferSize )
  458.                 {
  459.                     TotalOfInq = kInternalPBBufferSize;
  460.                 }
  461.                 
  462.                 ourPB->currentExecutionState = kInitInquiryDoneState;
  463.                 ourPB->completionProc = &InitStateMachine;
  464.                 err = InquiryBuilder( ourPB, ourPB->buffer, TotalOfInq );
  465.             }
  466.             else
  467.             {
  468.                 err = kClassNotConfiguredError;
  469.             }
  470.         }
  471.         break;
  472.         
  473.         case kInitInquiryDoneState:
  474.         {
  475.             if ( err != noErr)
  476.             {
  477.                 err = kClassNotConfiguredError;
  478.             }
  479.             else
  480.             {
  481.                 err = ProcessInquiryData( ourPB->buffer );
  482.             }
  483.         }
  484.         break;
  485.     }
  486.     
  487.     if ( err != kRequestPending )
  488.     {
  489.         UInt32                             theUserData;
  490.         ControlStatusCompletionProcPtr    theCallBack = (ControlStatusCompletionProcPtr) ourPB->userCompletionProc;
  491.         
  492.         theUserData = ourPB->userData;
  493.         FreeInternalPB( ourPB );
  494.         (*theCallBack) ( theUserData, err );
  495.     }
  496. }
  497.  
  498. OSStatus ProcessInquiryData( UInt8 *InquiryBuffer )
  499. {
  500.     UInt8                    loopCount;
  501.     UInt8                    replySize;
  502.     StandardInquiryDataPtr    inqReplyDataPtr;
  503.     
  504.     inqReplyDataPtr = (StandardInquiryDataPtr) InquiryBuffer;
  505.     
  506.     replySize = inqReplyDataPtr->additionalLength + 5;
  507.  
  508.     // Make sure that we only view what is in the buffer
  509.     // as part of the data.
  510.     if ( replySize > kInternalPBBufferSize )
  511.     {
  512.         replySize = kInternalPBBufferSize;
  513.     }
  514.     
  515.     // Check if there is a valid device connected.
  516.     if ( (inqReplyDataPtr->qualifierAndType & kInqPeripheralQualifierMask) != kInqPeripheralConnected )
  517.     {
  518.         // Check the Peripheral Qualifier to determine if there us a connected device at this LUN
  519.         // Since ATAPI does not support LUNs, this will be always be kInqPeripheralConnected for
  520.         // devices with an ATAPI interface.
  521.  
  522.         // If there is not a valid device, return an error and allow ourselves to be removed
  523.         return ioErr;
  524.     }
  525.     
  526.     // Check if the connected device is of a supported device class.
  527.     switch ( inqReplyDataPtr->qualifierAndType & kInqPeripheralDeviceTypeMask )
  528.     {
  529.         case kInqDirectAccessSBCDevice:
  530.         case kInqWriteOnceSBCDevice:
  531.         case kInqCDROM_MMCDevice:
  532.         case kInqOpticalMemorySBCDevice:
  533.         case kInqSimplifiedDirectAccessRBCDevice:
  534.         {
  535.             // The connected device is of a supported device class
  536.             // break and proceed with the processing
  537.             gPeripheralDeviceType = inqReplyDataPtr->qualifierAndType & kInqPeripheralDeviceTypeMask;
  538.         }
  539.         break;
  540.         
  541.         //kInqSequentialAccessSSCDevice            = 0x01,
  542.         //kInqPrinterSSCDevice                    = 0x02,
  543.         //kInqProcessorSPCDevice                    = 0x03,
  544.         //kInqScannerSCSI2Device                    = 0x06,
  545.         //kInqMediumChangerSMCDevice                = 0x08,
  546.         //kInqCommunicationsSSCDevice                = 0x09,
  547.         /* 0x0A - 0x0B ASC IT8 Graphic Arts Prepress Devices */
  548.         //kInqStorageArrayControllerSCC2Device    = 0x0C,
  549.         //kInqEnclosureServicesSESDevice            = 0x0D,
  550.         //kInqOpticalCardReaderOCRWDevice            = 0x0F,
  551.         /* 0x10 - 0x1E Reserved Device Types */
  552.         //kInqUnknownOrNoDeviceType                = 0x1F,
  553.         default:
  554.         {
  555.             // The device class is not supported, return an error
  556.             // and allow the driver to be removed.
  557.             return ioErr;
  558.         }
  559.         break;
  560.     }
  561.  
  562.     // This check is because the SanDisk does not return a Full Inquiry            
  563.     if ( replySize > 8)
  564.     {
  565.         // If we couldn't get a Vendor name string from the class driver,
  566.         // we need to build one now
  567.         if (gDeviceInfoStruct.vendorName == nil )
  568.         {
  569.             // Bytes 8-15, count 8
  570.             for( loopCount = 0; loopCount < kVendorIDLen; loopCount++)
  571.             {
  572.                 gVendorIDString[loopCount+1] = inqReplyDataPtr->vendorID[loopCount];
  573.             }
  574.             
  575.             // Remove any blank spaces at the end of the Vendor
  576.             // name if any exists.
  577.             for( loopCount; loopCount >= 0; loopCount--)
  578.             {
  579.                 if (gVendorIDString[loopCount+1] != 0x20)
  580.                 {
  581.                     break;
  582.                 }
  583.             }
  584.         
  585.             gVendorIDString[0] = loopCount;
  586.             gDeviceInfoStruct.vendorName = gVendorIDString;
  587.         }
  588.         
  589.         // If we couldn't get a Product name string from the class driver,
  590.         // use the one we just built
  591.         if (gDeviceInfoStruct.productName == nil )
  592.         {
  593.             // Bytes 16-31, count 16
  594.             // Build Product ID string.  We use this to determine if we have an LS-120
  595.             for( loopCount = 0; loopCount < kProductIDLen; loopCount++)
  596.             {
  597.                 gProductIDString[loopCount+1] = inqReplyDataPtr->productID[loopCount];
  598.             }
  599.             
  600.             // Remove any blank spaces at the end of the Product
  601.             // name if any exists.
  602.             for( loopCount; loopCount >= 0; loopCount--)
  603.             {
  604.                 if (gProductIDString[loopCount+1] != 0x20)
  605.                 {
  606.                     break;
  607.                 }
  608.             }
  609.             
  610.             gProductIDString[0] = loopCount;
  611.             gDeviceInfoStruct.productName = gProductIDString;
  612.         }
  613.         
  614.         // Get the Product Revison number
  615.         // Bytes 32-35 count 4
  616.         gFWRevisionString[0] = kProductRevLen;
  617.         for( loopCount = 0; loopCount < kProductRevLen; loopCount++)
  618.         {
  619.             gFWRevisionString[loopCount+1] = inqReplyDataPtr->productRevision[loopCount];
  620.         }
  621.         
  622.         gDeviceInfoStruct.revisionNumber = gFWRevisionString;
  623.         
  624.         // These use the Inquiry buffer since they are not currently defined
  625.         // in the Inquiry structure.
  626.         if ( inqReplyDataPtr->additionalLength > 31 )
  627.         {
  628.             // Get the subrevision number, Byte 36 count 1
  629.             gFWSubRevisionString[0] = 1;
  630.             gFWSubRevisionString[1] = InquiryBuffer[36];
  631.         
  632.             gDeviceInfoStruct.subRevisionNumber = gFWSubRevisionString;
  633.         }
  634.         
  635.         if ( inqReplyDataPtr->additionalLength == 123 )
  636.         {
  637.             // Get the Serial Number
  638.             gSerialNumberString[0] = 12; // Bytes 116-127, count 12
  639.             for( loopCount = 0; loopCount < 12; loopCount++)
  640.             {
  641.                 gSerialNumberString[loopCount+1] = InquiryBuffer[116+loopCount];
  642.             }
  643.         
  644.             gDeviceInfoStruct.serialNumber = gSerialNumberString;
  645.         }
  646.     }
  647.     
  648.     // Check if this is a Removable device
  649.     if ( (inqReplyDataPtr->removableBit & kInqRemovableMask) != 0)
  650.     {
  651.         gDriveIsFixed = false;
  652.         
  653.         // Check if this is the floppy subclass
  654.         if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
  655.         {
  656.             // If this is a floppy subclass device, we already know it supports floppies
  657.             gDriveSupportsFloppies = true;
  658.             
  659.             // Add floppies to the list of supported media types
  660.             gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgFloppyType;
  661.             gSupportedMediaTypes.supportTypesCount++;
  662.             
  663.             gDriveSupportsNonFloppyRemovable = false;
  664.         }
  665.         else
  666.         {
  667.             // Not a floppy subclass, so must be large capacity removables
  668.             
  669.             // But is it a CD/DVD Drive?
  670.             if (( inqReplyDataPtr->qualifierAndType & kInqPeripheralDeviceTypeMask ) == kInqCDROM_MMCDevice )
  671.             {
  672.                 // It is a CD Drive
  673.                 gDriveSupportsNonFloppyRemovable = true;
  674.                 gDriveSupportsFloppies = false;
  675.  
  676.                 gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgCDType;
  677.                 gSupportedMediaTypes.supportTypesCount++;
  678.             }
  679.             else
  680.             {
  681.                 // Not a CD Drive
  682.                 gDriveSupportsNonFloppyRemovable = true;
  683.  
  684.                 // Add Removables to the list of supported media types
  685.                 gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgRemovableType;
  686.                 gSupportedMediaTypes.supportTypesCount++;
  687.                 
  688.                 // Check if this drive supports floppy disk
  689.                 if ( kDriveSupportsFloppyDisk == true)
  690.                 {
  691.                     // This drive can also handle floppy disks
  692.                     gDriveSupportsFloppies = true;
  693.                     
  694.                     // Add floppies to the list of supported media types
  695.                     gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgFloppyType;
  696.                     gSupportedMediaTypes.supportTypesCount++;
  697.                 }
  698.                 else
  699.                 {
  700.                     // This is removable only device, cannot handle floppy disks
  701.                     gDriveSupportsFloppies = false;
  702.                 }
  703.             }
  704.         }
  705.     }
  706.     else
  707.     {
  708.         gDriveIsFixed = true;
  709.  
  710.         // Add Fixed Disk to the list of supported media types
  711.         gSupportedMediaTypes.supportedTypesArray[gSupportedMediaTypes.supportTypesCount] = kdgDiskType;
  712.         gSupportedMediaTypes.supportTypesCount++;
  713.         gDriveSupportsNonFloppyRemovable = false;
  714.     }
  715.  
  716.     // Check to determine which version of the commands should
  717.     // be used for the attached device.
  718.     gCommandSetANSIVersion = inqReplyDataPtr->version & kInqANSIVersionMask;
  719.     return noErr;
  720. }
  721.  
  722. OSStatus TerminateDriveAccess ( void )
  723. {
  724.     // Make sure that if there is a pending Manual Eject interrupt, it is cancelled
  725.     CancelManualEjectInterrupt();
  726.     
  727.     // Clear the strings out now that this device is gone
  728.     gDeviceInfoStruct.vendorName = nil;
  729.     gDeviceInfoStruct.productName = nil;
  730.     gDeviceInfoStruct.revisionNumber = nil;
  731.     gDeviceInfoStruct.subRevisionNumber = nil;
  732.     gDeviceInfoStruct.serialNumber = nil;
  733.  
  734.     while( IsCommandPending() == true )
  735.     {
  736.         //SysDebugStr("\pWait until done");
  737.         // Wait for the pending command to finish before 
  738.         // proceeding with the terminate
  739.     }
  740.     
  741.     (void) TerminateClassAccess();
  742.  
  743.     return noErr;
  744. }
  745.  
  746. #pragma mark -
  747. #pragma mark Check For Media Routines
  748. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  749. // Check For Media
  750. // All functions and variables needed to check for the presence
  751. // of media in the attached device.
  752. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  753.  
  754. // State Machine States for the Check For Media Machine
  755. enum
  756. {
  757.     kCheckStartState = 1,
  758.     kCheckTURDoneState,
  759.     kYEDataCheckDoneState,
  760.     kCheckReqSenseDoneState,
  761.     kCheckZipNonSenseDoneState,
  762.     kCheckFloppyCheckFormatsState
  763. };
  764.  
  765. static void CheckMediaMachine ( IntDriveRequestPB *ourPB );
  766.  
  767. enum
  768. {
  769.     kGetGeometryStartState = 1,
  770.     kGetGeometryDoneState,
  771.     kGetGeometryErrorDone,
  772.     kGetGeometryPossibleCapacitiesDone
  773. };
  774.  
  775. static void GetGeometryStateMachine( IntDriveRequestPBPtr ourPB );
  776.  
  777. // State Machine States for the Check For Media Machine
  778. enum
  779. {
  780.     kWriteProtectStartState = 1,
  781.     kWriteProtectModeSenseDoneState,
  782.     kWriteProtectReqSenseDoneState
  783. };
  784.  
  785. static void CheckWriteProtectMachine( IntDriveRequestPB *ourPB );
  786.  
  787. // External Device Access Functions
  788. OSStatus CheckIfMediaIsPresent( UInt32 userData, CheckIfMediaIsPresentCompletionProcPtr callBack )
  789. {
  790.     IntDriveRequestPBPtr     ourPB;
  791.  
  792.     if ( IsDeviceAccessEnabled() == false )
  793.     {
  794.         return kDeviceAccessNoMediaError;
  795.     }
  796.     
  797.     if ( gWaitingForEjectCompletion == true )
  798.     {
  799.         // We are currently waiting for manual eject media 
  800.         // to be removed, until it is, we won't try to remount.
  801.         return kDeviceAccessNoMediaError;
  802.     }
  803.     
  804.     ourPB = GetDriveInternalPB();
  805.     if ( ourPB == nil )
  806.     {
  807.         return kDeviceAccessNoMediaError;
  808.     }
  809.     
  810.     ourPB->userData = userData;
  811.     ourPB->userCompletionProc = callBack;
  812.     ourPB->currentExecutionState = kCheckStartState;
  813.     ourPB->completionProc = &CheckMediaMachine;
  814.     ourPB->status = noErr;
  815.     CheckMediaMachine( ourPB );
  816.  
  817.     return kRequestPending;
  818. }
  819.  
  820. void CheckMediaMachine( IntDriveRequestPB *ourPB )
  821. {
  822.     OSStatus             err = ourPB->status;
  823.  
  824.     switch (ourPB->currentExecutionState )
  825.     {
  826.         case kCheckStartState:
  827.         {
  828.             if (( gDriveIsFixed == true ) && ( gRemountFixed == false ))
  829.             {
  830.                 err = kDeviceAccessNoMediaError;
  831.                 break;
  832.             }
  833.             
  834.             ourPB->completionProc = &CheckMediaMachine;
  835.             ourPB->currentExecutionState = kCheckTURDoneState;
  836.             
  837.             err = TestUnitReadyBuilder( ourPB );
  838.         }
  839.         break;
  840.  
  841.         case kCheckTURDoneState:
  842.         {
  843.             ourPB->currentExecutionState = kCheckReqSenseDoneState;
  844.             ourPB->completionProc = &CheckMediaMachine;
  845.             err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
  846.         }
  847.         break;
  848.         
  849.         case kCheckReqSenseDoneState:
  850.         {
  851.             FixRequestSenseData( ourPB );
  852.             err = ourPB->status;
  853.             if (err == noErr)
  854.             {
  855.                 // Check the Sense Key to see if it an error group we know how to handle.
  856.                 if (((ourPB->buffer[2] & 0x0F) == 0x02) || ((ourPB->buffer[2] & 0x0F) == 0x03))
  857.                 {
  858.                     // The Sense Key is an 02 (Not Ready) or 03 (Medium Error), check to see if there 
  859.                     // is something that needs to be done to fix this.
  860.                     if ((ourPB->buffer[12] == 0x04) && (ourPB->buffer[13] == 0x02))
  861.                     {
  862.                         // We need to send a start command, then restart the machine
  863.                         ourPB->currentExecutionState = kCheckStartState;
  864.                         ourPB->completionProc = &CheckMediaMachine;
  865.                         err = StartStopEjectCartridgeBuilder( ourPB, kStartDevice, kDontEjectMedia);
  866.                         break;
  867.                     }
  868.                     else if ((ourPB->buffer[12] == 0x30) && ((ourPB->buffer[13] == 0x00) || (ourPB->buffer[13] == 0x02)))
  869.                     {
  870.                         // The drive says the media is incompatible, with this device, inform the User and 
  871.                         // inform UnitTable module.
  872.                         ShowUnusableMediaDialog();
  873.                         err = kDeviceAccessMediaUnusable;
  874.                         break;
  875.                     }
  876.                     else if (((ourPB->buffer[12] == 0x30) && (ourPB->buffer[13] == 0x01)) 
  877.                         || ((ourPB->buffer[12] == 0x31) && (ourPB->buffer[13] == 0x00)))
  878.                     {
  879.                         // The device says the media has an unknown or corrupted format.
  880.                         // It may needed to be formatted or reformatted.  Act like we have 
  881.                         // good media and let Finder display the please initialize dialog for 
  882.                         // the user.  No need to do anything here, it will be handled below.
  883.                     }
  884.                     else
  885.                     {
  886.                         // The drive is not ready.  This means that either there is no media in the device,
  887.                         // or the device may be spinning the media up.  In either case, act like there is
  888.                         // no media and the UnitTable module will call use again after a delay.
  889.                         err = kDeviceAccessNoMediaError;
  890.                         break;
  891.                     }
  892.                 }
  893.                 else if ((ourPB->buffer[2] & 0x0F) > 0x01)
  894.                 {
  895.                     // If the Sense Key is anything but 00 (No Sense), 01 (Recovered Error) or
  896.                     // 03 (Medium Error) it is an error that we cannot handle, return no Media
  897.                     err = kDeviceAccessNoMediaError;
  898.                     break;
  899.                 }
  900.  
  901.                 // It appears we have media and the drive is ready, let the UnitTable Module
  902.                 // try to mount it.
  903.                 if ( WasManualEjectRemoveIssued() == true )
  904.                 {    
  905.                     RemoveCurrentDialog();
  906.                     ClearManualEjectRemoveIssued();
  907.                 }
  908.     
  909.                 if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
  910.                 {
  911.                     // If this is the floppy subclass device, check formats
  912.                     // capacity data to determine if drive is ready.
  913.                     ourPB->currentExecutionState = kCheckFloppyCheckFormatsState;
  914.                     ourPB->completionProc = &CheckMediaMachine;
  915.                     err = ReadFormatCapacityBuilder( ourPB, ourPB->buffer, 0x0C);
  916.                 }
  917.             }
  918.             else
  919.             {
  920.                 err = kDeviceAccessNoMediaError;
  921.                 
  922.             }
  923.         }
  924.         break;
  925.  
  926.         case kCheckZipNonSenseDoneState:
  927.         {
  928.             RemoveCurrentDialog();
  929.             if (err == noErr)
  930.             {
  931.                 if ( ourPB->buffer[21] == 0x05 )
  932.                 {
  933.                     // This cartridge is Read protected, inform the User and inform UnitTable module
  934.                     ShowUnusableMediaDialog();
  935.                     err = kDeviceAccessMediaUnusable;
  936.                 }
  937.                 else
  938.                 {
  939.                     err = noErr;
  940.                 }
  941.             }
  942.             else
  943.             {
  944.                 err = kDeviceAccessNoMediaError;
  945.             }
  946.         }
  947.         break;
  948.                 
  949.         case kCheckFloppyCheckFormatsState:
  950.         {
  951.             if (( ourPB->buffer[3] == 0x03 ) || (err != noErr))
  952.             {
  953.                 // There is currently no media in the drive
  954.                 err = kDeviceAccessNoMediaError;
  955.             }
  956.             else
  957.             {
  958.                 // There is media in the drive
  959.                 err = noErr;
  960.             }
  961.         }
  962.         break;
  963.     }
  964.     
  965.     if ( err != kRequestPending )
  966.     {
  967.         
  968. #if 0
  969.         if ( err == noErr )
  970.         {
  971.             // We have media in the driver inform the class driver, that it
  972.             // should not be replace.
  973.             UInt8        lockRequest = kLockROMDriver;
  974.             OSStatus    status;
  975.             
  976.             status = SendClassControlCall(kUSBStorageControlLockROMDriver, &lockRequest);
  977.             // If the driver can't be locked, should we post an error to keep the media from mounting and to
  978.             // avoid the drivers from being replaced after a PostEvent is made?
  979.         }
  980. #endif
  981.  
  982.         if ( err == noErr )
  983.         {
  984.             // Media is in the drive and no errors has
  985.             // occurred, check the disk capacity.
  986.             ourPB->status = noErr;
  987.             ourPB->currentExecutionState = kGetGeometryStartState;
  988.             ourPB->completionProc = &GetGeometryStateMachine;
  989.             GetGeometryStateMachine( ourPB );
  990.         }
  991.         else
  992.         {
  993.             // An error occurred, no reason to proceed to the check capacity routine.
  994.             UInt32                                     theUserData;
  995.             CheckIfMediaIsPresentCompletionProcPtr    theCallBack = (CheckIfMediaIsPresentCompletionProcPtr) ourPB->userCompletionProc;
  996.         
  997.             theUserData = ourPB->userData;
  998.             FreeInternalPB( ourPB );
  999.             (*theCallBack) ( theUserData, err);
  1000.         }
  1001.     }
  1002. }
  1003.  
  1004. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1005. // Get Media Capacity
  1006. // All functions and variables needed to handle the determination
  1007. // of the capacity of the media in the attached device.
  1008. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1009. void GetGeometryStateMachine( IntDriveRequestPBPtr ourPB )
  1010. {
  1011.     OSStatus    err = ourPB->status;
  1012.     
  1013.     switch (ourPB->currentExecutionState)
  1014.     {
  1015.         case kGetGeometryStartState:
  1016.         {
  1017.             gCurrentMediaCapacity.TotalBlocksOnMedia = 0;
  1018.             gCurrentMediaCapacity.BlockLengthInBytes = 0;
  1019.             ourPB->currentExecutionState = kGetGeometryDoneState;
  1020.             ourPB->completionProc = &GetGeometryStateMachine;
  1021.             if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
  1022.             {
  1023.                 // We have a floppy drive, do a Read Format capacitities
  1024.                 err = ReadFormatCapacityBuilder( ourPB, ourPB->buffer, 0x0C);
  1025.             }
  1026.             else
  1027.             {
  1028.                 err = GetMediaGeometryBuilder( ourPB, &gCurrentMediaCapacity );
  1029.             }
  1030.         }
  1031.         break;
  1032.         
  1033.         case kGetGeometryDoneState:
  1034.         {
  1035.             if (gDeviceUSBSubClass == kUSBStorageUFISubclass )
  1036.             {
  1037.                 gCurrentMediaCapacity.TotalBlocksOnMedia = *((UInt32 *) &ourPB->buffer[4]);
  1038.                 gCurrentMediaCapacity.BlockLengthInBytes = *((UInt16 *) &ourPB->buffer[10]);
  1039.             }
  1040.             else
  1041.             {
  1042.                 // Need to add one because GetCapacity command returns last block number, not number of blocks
  1043.                 gCurrentMediaCapacity.TotalBlocksOnMedia +=1; 
  1044.             }
  1045.             
  1046.             if((gCurrentMediaCapacity.TotalBlocksOnMedia == 0) || (gCurrentMediaCapacity.BlockLengthInBytes == 0))
  1047.             {
  1048.                 //Find out why the capacity is zero
  1049.                 ourPB->currentExecutionState = kGetGeometryErrorDone;
  1050.                 ourPB->completionProc = &GetGeometryStateMachine;
  1051.                 err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
  1052.             }
  1053.             else
  1054.             {
  1055.                 err = noErr;
  1056.             }
  1057.         }
  1058.         break;
  1059.  
  1060.         case kGetGeometryErrorDone:
  1061.         {
  1062.             if( (ourPB->buffer[12] == 0x30) && (ourPB->buffer[13] == 0x01))
  1063.             {
  1064.                 // We have an unformatted cartridge, find out what format it can have
  1065.                 ourPB->currentExecutionState = kGetGeometryPossibleCapacitiesDone;
  1066.                 ourPB->completionProc = &GetGeometryStateMachine;
  1067.                 err = ReadFormatCapacityBuilder(ourPB, ourPB->buffer, 0x0C);
  1068.             }
  1069.             else
  1070.             {
  1071.                 err = kDeviceAccessNoMediaError;
  1072.             }
  1073.         }
  1074.         break;
  1075.         
  1076.         case kGetGeometryPossibleCapacitiesDone:
  1077.         {
  1078.             FixRequestSenseData( ourPB );
  1079.             err = ourPB->status;
  1080.             if( (ourPB->buffer[3] >= 0x08) && (ourPB->buffer[8] & 0x01 == 0x01))
  1081.             {
  1082.                 // this is the maximum format for this cartridge
  1083.                 gCurrentMediaCapacity.TotalBlocksOnMedia = *((UInt32 *) &ourPB->buffer[4]);
  1084.                 gCurrentMediaCapacity.BlockLengthInBytes = *((UInt16 *) &ourPB->buffer[10]);
  1085.                 
  1086.                 err = noErr;
  1087.             }
  1088.             else
  1089.             {
  1090.                 err = kDeviceAccessNoMediaError;
  1091.             }
  1092.         }
  1093.         break;
  1094.     }
  1095.     
  1096.     if ( err != kRequestPending )
  1097.     {
  1098.         if ( err == noErr )
  1099.         {
  1100.             // Media is in the drive, disk capacity was checked and, 
  1101.             // so far, no errors have occurred, check if media is write protected.
  1102.             ourPB->currentExecutionState = kWriteProtectStartState;
  1103.             ourPB->completionProc = &CheckWriteProtectMachine;
  1104.             ourPB->status = noErr;
  1105.             CheckWriteProtectMachine( ourPB );
  1106.         }
  1107.         else
  1108.         {
  1109.             // An error occurred, no reason to proceed to the check write protect routine.
  1110.             UInt32                                     theUserData;
  1111.             CheckIfMediaIsPresentCompletionProcPtr    theCallBack = (CheckIfMediaIsPresentCompletionProcPtr) ourPB->userCompletionProc;
  1112.         
  1113.             theUserData = ourPB->userData;
  1114.             FreeInternalPB( ourPB );
  1115.             (*theCallBack) ( theUserData, err);
  1116.         }
  1117.     }
  1118. }
  1119.  
  1120. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1121. // Check For Write Protect
  1122. // All functions and variables needed to check whether the 
  1123. // media in the attached device is write protected.
  1124. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1125. void CheckWriteProtectMachine( IntDriveRequestPB *ourPB )
  1126. {
  1127.     OSStatus             err = ourPB->status;
  1128.  
  1129.     switch (ourPB->currentExecutionState )
  1130.     {
  1131.         case kWriteProtectStartState:
  1132.         {
  1133.             // Check to see if media is write protected
  1134.             ourPB->currentExecutionState = kWriteProtectModeSenseDoneState;
  1135.             ourPB->completionProc = &CheckWriteProtectMachine;
  1136.  
  1137.             if ( gPeripheralDeviceType == kInqWriteOnceSBCDevice )
  1138.             {
  1139.                 // This is a write once type device.  In order to be safe and not waste
  1140.                 // space on the cartridge, the driver will report this media as Write Protected.
  1141.                 gMediaIsWriteProtected = true;
  1142.                 err = noErr;
  1143.             }
  1144.             else if ( gPeripheralDeviceType == kInqCDROM_MMCDevice )
  1145.             {
  1146.                 // This is a CD or DVD drive, should determine depending
  1147.                 // on media type.  For now, it is always write protected.
  1148.                 gMediaIsWriteProtected = true;
  1149.                 err = noErr;
  1150.             }
  1151.             else if ( gPeripheralDeviceType == kInqSimplifiedDirectAccessRBCDevice )
  1152.             {
  1153.                 // We have a RBC SCSI device, use the 6 byte version of the command and
  1154.                 // check the RBC Device Parameter page.
  1155.                 err = ModeSenseBuilder(ourPB, kRBCCommand, ourPB->buffer, kRBCDeviceParametersModePageCode, 
  1156.                         kMode6ParameterHeaderLength+kRBCDeviceParametersModePageLength);
  1157.             }
  1158.             else if ( gCommandSetANSIVersion > 0 )
  1159.             {
  1160.                 // We have a non MMC SCSI device, use the 6 byte version of the command
  1161.                 err = ModeSenseBuilder(ourPB, kRBCCommand, ourPB->buffer, kAllModePages, kMode6ParameterHeaderLength);
  1162.             }
  1163.             else
  1164.             {
  1165.                 // This is a non MMC ATAPI device, use the 10 byte version.
  1166.                 err = ModeSenseBuilder(ourPB, kSPCCommand, ourPB->buffer, kAllModePages, kMode10ParameterHeaderLength);
  1167.             }
  1168.         }
  1169.         break;
  1170.     
  1171.         case kWriteProtectModeSenseDoneState:
  1172.         {
  1173.             if ( err == noErr )
  1174.             {
  1175.                 if ( gPeripheralDeviceType == kInqSimplifiedDirectAccessRBCDevice )
  1176.                 {
  1177.                     // This is an RBC device, check the RBC Device Parameter mode page.
  1178.                     // Check byte 11, bit 2 in the page descriptor.
  1179.                     if (( ourPB->buffer[11 + kMode6ParameterHeaderLength] & 0x04 ) != 0 )
  1180.                     {
  1181.                         gMediaIsWriteProtected = true;
  1182.                         err = noErr;
  1183.                     }
  1184.                     else
  1185.                     {
  1186.                         gMediaIsWriteProtected = false;
  1187.                         err = noErr;
  1188.                     }
  1189.                 }
  1190.                 else if ((( ourPB->executePB.cdb[0] == kCmdModeSense6 ) && (( ourPB->buffer[2] & 0x80 ) != 0 )) 
  1191.                     || (( ourPB->executePB.cdb[0] == kCmdModeSense10 ) && (( ourPB->buffer[3] & 0x80 ) != 0 )))
  1192.                 {
  1193.                     gMediaIsWriteProtected = true;
  1194.                     err = noErr;
  1195.                 }
  1196.                 else
  1197.                 {
  1198.                     gMediaIsWriteProtected = false;
  1199.                     err = noErr;
  1200.                 }
  1201.             }
  1202.             else
  1203.             {
  1204.                 // Since an error occurred while trying to determine if the media is
  1205.                 // write protected, say that it is just to be on the safe side.
  1206.                 gMediaIsWriteProtected = true;
  1207.                 ourPB->currentExecutionState = kWriteProtectReqSenseDoneState;
  1208.                 ourPB->completionProc = &CheckWriteProtectMachine;
  1209.                 err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
  1210.             }
  1211.         }
  1212.         break;
  1213.  
  1214.         case kWriteProtectReqSenseDoneState:
  1215.         {
  1216.             err = noErr;
  1217.         }
  1218.         break;
  1219.     }
  1220.     
  1221.     if ( err != kRequestPending )
  1222.     {
  1223.         UInt32                                     theUserData;
  1224.         CheckIfMediaIsPresentCompletionProcPtr    theCallBack = (CheckIfMediaIsPresentCompletionProcPtr) ourPB->userCompletionProc;
  1225.         
  1226.         theUserData = ourPB->userData;
  1227.         FreeInternalPB( ourPB );
  1228.         (*theCallBack) (theUserData, err);
  1229.     }
  1230. }
  1231.  
  1232. #pragma mark -
  1233. #pragma mark Eject State Machine
  1234.  
  1235. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1236. // Eject the Media
  1237. // All functions and variables needed to handle the ejection
  1238. // of media in the attached device.
  1239. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1240. // Eject State machine states
  1241. enum
  1242. {
  1243.     kEjectProcessStartState = 1,
  1244.     kEjectCommandDoneState,
  1245.     kEjectReqSenseDoneState,
  1246.     kManEjectStartState,
  1247.     kManEjectTURDoneState,
  1248.     kManEjectReqSenseDoneState
  1249. };
  1250.  
  1251. static void HandleEjectStateMachine( IntDriveRequestPBPtr ourPB );
  1252. static OSStatus EjectCheckInterrupt( void *p1, void *p2);
  1253. static TimerID    EjectCheckInterruptTimer = 0;
  1254.  
  1255. OSStatus EjectCartridge( UInt32 userData, EjectCartridgeCompletionProcPtr callBack )
  1256. {
  1257. #pragma unused ( userData, callBack )
  1258.     IntDriveRequestPBPtr     ourPB;
  1259.  
  1260.     ClearManualEjectInterrupt();
  1261.  
  1262.     gCurrentMediaCapacity.TotalBlocksOnMedia = 0;
  1263.     gCurrentMediaCapacity.BlockLengthInBytes = 0;
  1264.  
  1265.     if ( gDriveIsFixed == true )
  1266.     {
  1267.         gRemountFixed = false;
  1268.         
  1269.         // Make the system think that we ejected the disk
  1270.         return noErr;
  1271.     }
  1272.     
  1273.     if ( IsDeviceAccessEnabled() == false )
  1274.     {
  1275.         // Make the system think that we ejected the disk
  1276.         return noErr;
  1277.     }
  1278.  
  1279.     if ( WasManualEjectRemoveIssued() == true )
  1280.     {    
  1281.         // The disk was already removed, no need to try eject.
  1282.         ClearManualEjectRemoveIssued();
  1283.         return noErr;
  1284.     }
  1285.  
  1286.     gWaitingForEjectCompletion = true;
  1287.     ourPB = GetDriveInternalPB();
  1288.     
  1289.     // Since we will immediately call back, no need to save this information
  1290. /*    ourPB->userData = userData;*/
  1291. /*    ourPB->userCompletionProc = callBack;*/
  1292.  
  1293.     ourPB->currentExecutionState = kEjectProcessStartState;
  1294.     if(( IsDeviceKnownManualEject() == true ) || ( gPreventCommandFailed == true ))
  1295.     {
  1296.         // This device is manual eject, Display dialog and wait for user to remove disk
  1297.         ShowPleaseEjectMediaDialog();
  1298.         ourPB->currentExecutionState = kManEjectStartState;
  1299.     }
  1300.     else
  1301.     {
  1302.         ourPB->currentExecutionState = kEjectProcessStartState;
  1303.     }
  1304.     
  1305.     ourPB->completionProc = &HandleEjectStateMachine;
  1306.     HandleEjectStateMachine( ourPB );
  1307.  
  1308.     return noErr;
  1309. }
  1310.  
  1311. OSStatus EjectCheckInterrupt( void *p1, void *p2)
  1312. {
  1313. #pragma unused ( p2 )
  1314.     IntDriveRequestPBPtr    ourPB;
  1315.     
  1316.     EjectCheckInterruptTimer = 0;
  1317.     ourPB = (IntDriveRequestPBPtr) p1;
  1318.     ourPB->currentExecutionState = kManEjectStartState;
  1319.     ourPB->status = noErr;
  1320.     HandleEjectStateMachine( ourPB );
  1321.  
  1322.      return noErr;
  1323. }
  1324.  
  1325.  
  1326. void HandleEjectStateMachine( IntDriveRequestPBPtr ourPB )
  1327. {
  1328.     OSStatus                err = ourPB->status;
  1329.     Boolean                    resetTheInterrupt = false;
  1330.     
  1331.     switch (ourPB->currentExecutionState)
  1332.     {
  1333.         case kEjectProcessStartState:
  1334.         {
  1335.             ourPB->currentExecutionState = kEjectCommandDoneState;
  1336.             ourPB->completionProc = &HandleEjectStateMachine;
  1337.             err = StartStopEjectCartridgeBuilder( ourPB, kStopDevice, kEjectMedia);
  1338.         }
  1339.         break;
  1340.         
  1341.         case kEjectCommandDoneState:
  1342.         {
  1343.             if ( ourPB->autoStatusIsValid == true )
  1344.             {
  1345.                 // We have a device that reports the sense in the interrupt, don't need to do Req Sense
  1346.                 if (((ourPB->autoStatus[0] == 0x24) && (ourPB->status == noErr)) || ( IsDeviceKnownManualEject() == true ) || (    gPreventCommandFailed == true))
  1347.                 {
  1348.                     // Don't make the caller wait for the media to be removed.
  1349. /*                    if ( ourPB->userCompletionProc != nil )*/
  1350. /*                    {*/
  1351. /*                        (*(EjectCartridgeCompletionProcPtr) ourPB->userCompletionProc) ( ourPB->userData, err );*/
  1352. /*                        ourPB->userCompletionProc = nil;*/
  1353. /*                        ourPB->userData = 0;*/
  1354. /*                    }*/
  1355.         
  1356.                     ShowPleaseEjectMediaDialog();
  1357.                     ourPB->currentExecutionState = kManEjectTURDoneState;
  1358.                     ourPB->completionProc = &HandleEjectStateMachine;
  1359.                     err = TestUnitReadyBuilder( ourPB );
  1360.                 }
  1361.                 else
  1362.                 {
  1363.                     // The cartridge was ejected
  1364.                     err = noErr;
  1365.                 }
  1366.             }
  1367.             else
  1368.             {
  1369.                 // We need issue a request sense to get sense data
  1370.                 ourPB->currentExecutionState = kEjectReqSenseDoneState;
  1371.                 ourPB->completionProc = &HandleEjectStateMachine;
  1372.                 err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
  1373.             }
  1374.         }
  1375.         break;
  1376.         
  1377.         case kEjectReqSenseDoneState:
  1378.         {
  1379.             FixRequestSenseData( ourPB );
  1380.             err = ourPB->status;
  1381.             if(((ourPB->buffer[12] == 0x24) && ( err == noErr )) || ( IsDeviceKnownManualEject() == true ) || ( gPreventCommandFailed == true ))
  1382.             {
  1383.                 // This device is manual eject, Display dialog and wait for user to remove disk
  1384. /*                if ( ourPB->userCompletionProc != nil )*/
  1385. /*                {*/
  1386. /*                    (*(EjectCartridgeCompletionProcPtr) ourPB->userCompletionProc) ( ourPB->userData, err );*/
  1387. /*                    ourPB->userCompletionProc = nil;*/
  1388. /*                    ourPB->userData = 0;*/
  1389. /*                }*/
  1390.                 ShowPleaseEjectMediaDialog();
  1391.                 ourPB->currentExecutionState = kManEjectTURDoneState;
  1392.                 ourPB->completionProc = &HandleEjectStateMachine;
  1393.  
  1394.                 err = TestUnitReadyBuilder( ourPB );
  1395.             
  1396.             }
  1397.             else
  1398.             {
  1399.                 // The cartridge was ejected
  1400.                 err = noErr;
  1401.             }
  1402.         }
  1403.         break;
  1404.         
  1405.         case kManEjectStartState:
  1406.         {
  1407.             ourPB->currentExecutionState = kManEjectTURDoneState;
  1408.             ourPB->completionProc = &HandleEjectStateMachine;
  1409.             err = TestUnitReadyBuilder( ourPB );
  1410.             
  1411.         }
  1412.         break;
  1413.  
  1414.         case kManEjectTURDoneState:
  1415.         {
  1416.             if ( ourPB->autoStatusIsValid == true )
  1417.             {
  1418.                 // We have a device that reports the sense in the interrupt, don't need to do Req Sense
  1419.                 if ((ourPB->autoStatus[0] != 0x00) && (ourPB->status == noErr))
  1420.                 {
  1421.                     RemoveCurrentDialog();
  1422.                     err = noErr;
  1423.                 }
  1424.                 else
  1425.                 {
  1426.                     resetTheInterrupt = true;
  1427.                 }
  1428.             }
  1429.             else
  1430.             {
  1431.                 // We need issue a request sense to get sense data
  1432.                 ourPB->currentExecutionState = kManEjectReqSenseDoneState;
  1433.                 ourPB->completionProc = &HandleEjectStateMachine;
  1434.                 err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
  1435.             }
  1436.         }
  1437.         break;
  1438.         
  1439.         case kManEjectReqSenseDoneState:
  1440.         {
  1441.             FixRequestSenseData( ourPB );
  1442.             err = ourPB->status;
  1443.             if(((ourPB->buffer[12] == 0x28) || (ourPB->buffer[12] == 0x3A)) && ( err == noErr))
  1444.             {
  1445.                 // The drive returned sense data and indicates that either there is media currently
  1446.                 // in the drive, or the media in the drive has been changed since the last Request
  1447.                 // Sense command was issued.  In either case, remove the dialog and leave this state
  1448.                 // machine.
  1449.                 RemoveCurrentDialog();
  1450.                 err = noErr;
  1451.             }
  1452.             else
  1453.             {
  1454.                 resetTheInterrupt = true;
  1455.             }
  1456.         }
  1457.         break;
  1458.     }
  1459.  
  1460.     if ( err != kRequestPending )
  1461.     {
  1462.         gPreventCommandFailed = false;
  1463.         gWaitingForEjectCompletion = false;
  1464.         
  1465.         if ( resetTheInterrupt == true )
  1466.         {
  1467.             AbsoluteTime        theWait;
  1468.         
  1469.             theWait = DurationToAbsolute( durationSecond );
  1470.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1471.             SetInterruptTimer( &theWait, &EjectCheckInterrupt, ourPB, &EjectCheckInterruptTimer);
  1472.         }
  1473.         else
  1474.         {
  1475.             // We have finished, free the PB, so it can be used by others.
  1476.             FreeInternalPB( ourPB );
  1477.         }
  1478.     }
  1479. }
  1480.  
  1481. #pragma mark -
  1482.  
  1483. static void PreventRequestCompletion(  IntDriveRequestPBPtr requestPB  );
  1484.  
  1485. OSStatus PreventMediaRemoval( UInt32    userData, LockMediaCompletionProcPtr callBack ) 
  1486. {
  1487.     IntDriveRequestPBPtr     ourPB;
  1488.     OSStatus                err = noErr;
  1489.  
  1490.     ourPB = GetDriveInternalPB();
  1491.     
  1492.     ourPB->userData = userData;
  1493.     ourPB->userCompletionProc = callBack;
  1494.  
  1495.     ourPB->completionProc = &PreventRequestCompletion;
  1496.  
  1497.     // We already know this is manual eject, or we know that it definitely is
  1498.     // not manual ejectable.
  1499.     if (( IsDeviceKnownManualEject() == true ) || ( gDriveIsFixed == true ))
  1500.     {
  1501.         err = TestUnitReadyBuilder( ourPB );
  1502.     }
  1503.     else
  1504.     {
  1505.         err = PreventAllowRemovalBuilder( ourPB, kPreventMediaRemoval );
  1506.     }
  1507.     
  1508.     if ( err != kRequestPending )
  1509.     {
  1510.         FreeInternalPB( ourPB );
  1511.     }
  1512.     
  1513.     return err;
  1514. }
  1515.  
  1516. void PreventRequestCompletion( IntDriveRequestPBPtr requestPB )
  1517. {
  1518.     // If the Prevent command was illegal, we have a manual eject disk
  1519.     // set the interrupt up to start
  1520.     OSStatus                    theStatus = requestPB->status;
  1521.     LockMediaCompletionProcPtr    theCallBack = (LockMediaCompletionProcPtr) requestPB->userCompletionProc;
  1522.     UInt32                        theUserData = requestPB->userData;
  1523.     
  1524.     //Set the polling interrupt
  1525.     if((( theStatus != noErr ) || (IsDeviceKnownManualEject() == true)) && ( gDriveIsFixed == false ))
  1526.     {
  1527.         gPreventCommandFailed = true;
  1528.         SetManualCheckInterrupt();
  1529.     }
  1530.     else
  1531.     {
  1532.         gPreventCommandFailed = false;
  1533.     }
  1534.     
  1535.     FreeInternalPB( requestPB );
  1536.     (*theCallBack) ( theUserData, theStatus );
  1537. }
  1538.  
  1539. OSStatus AllowMediaRemoval( UInt32 userData, UnlockMediaCompletionProcPtr callBack )
  1540. {
  1541.     OSStatus                err = noErr;
  1542.  
  1543.     if (( IsDeviceKnownManualEject() == true ) || ( gPreventCommandFailed == true ))
  1544.     {
  1545.         // Media is not locked in drive so no allow media
  1546.         // removal command is needed.  Return an immediate noErr.
  1547.         err = noErr;
  1548.     }
  1549.     else
  1550.     {
  1551.         // The media was successfully locked into the drive so an allow
  1552.         // media removal command is needed before an eject can be done.
  1553.         IntDriveRequestPBPtr     ourPB;
  1554.         
  1555.         ourPB = GetDriveInternalPB();
  1556.         
  1557.         ourPB->userData             = userData;
  1558.         ourPB->userCompletionProc     = callBack;
  1559.         ourPB->completionProc         = &ExternalRequestComplete;
  1560.     
  1561.         err = PreventAllowRemovalBuilder( ourPB, kAllowMediaRemoval );
  1562.         if ( err != kRequestPending )
  1563.         {
  1564.             FreeInternalPB( ourPB );
  1565.         }
  1566.     }
  1567.     
  1568.     return err;
  1569. }
  1570.  
  1571. void ExternalRequestComplete ( IntDriveRequestPBPtr requestPB )
  1572. {
  1573.     DefaultDAMCompletionProcPtr        theCallBack = (DefaultDAMCompletionProcPtr) requestPB->userCompletionProc;
  1574.     OSStatus                        theStatus = requestPB->status;
  1575.     UInt32                            theUserData = requestPB->userData;
  1576.  
  1577.     FreeInternalPB( requestPB );
  1578.     (*theCallBack) ( theUserData, theStatus );
  1579. }
  1580.  
  1581. void ReadWriteRequestComplete ( IntDriveRequestPBPtr requestPB )
  1582. {
  1583.     OSStatus                        theStatus = requestPB->status;
  1584.  
  1585.     if ( requestPB->status == noErr )
  1586.     {
  1587.         if (requestPB->isWriteRequest == true ) 
  1588.         {
  1589.             // This was a write command.  Make sure that no error occurred
  1590.             // by doing a request sense on the device.
  1591.             requestPB->completionProc = &WriteRequestSenseComplete;
  1592.         
  1593.             theStatus =  RequestSenseBuilder( requestPB, (Ptr) requestPB->buffer );
  1594.         }
  1595.         else
  1596.         {
  1597.             // This was a read request and no error occurred,
  1598.             // call the client's completion with a good status
  1599.             theStatus = noErr;
  1600.         }
  1601.     }
  1602.     else
  1603.     {
  1604.         // An error occurred, get the request sense to make sure that the device
  1605.         // is still in a usuable state. (Some USB devices require a RequestSense to clear
  1606.         // internal error conditions.
  1607.         requestPB->completionProc = &ReadWriteRequestSenseOnErrorComplete;
  1608.     
  1609.         theStatus =  RequestSenseBuilder( requestPB, (Ptr) requestPB->buffer );
  1610.     }
  1611.     
  1612.     if ( theStatus != kRequestPending )
  1613.     {
  1614.         DefaultDAMCompletionProcPtr        theCallBack = (DefaultDAMCompletionProcPtr) requestPB->userCompletionProc;
  1615.         UInt32                            theUserData = requestPB->userData;
  1616.         
  1617.         // Since the status is not kRequestPending, this request must be finished.
  1618.         // Transfer back the status to the client.
  1619.         FreeInternalPB( requestPB );
  1620.         (*theCallBack) ( theUserData, theStatus );
  1621.     }
  1622. }
  1623.  
  1624. void WriteRequestSenseComplete ( IntDriveRequestPBPtr requestPB )
  1625. {
  1626.     DefaultDAMCompletionProcPtr        theCallBack = (DefaultDAMCompletionProcPtr) requestPB->userCompletionProc;
  1627.     OSStatus                        returnStatus = ioErr;
  1628.     UInt32                            theUserData = requestPB->userData;
  1629.  
  1630.     FixRequestSenseData( requestPB );
  1631.     if ( requestPB->status == noErr )
  1632.     {
  1633.         // If no error occurred on the request sense, check the sense to see
  1634.         // if an error occurred on the original request.
  1635.         if (( requestPB->buffer[2] & 0x0F !=0 ) && ( requestPB->buffer[2] & 0x0F !=1 ))
  1636.         {
  1637.             // An error has been reported back in the sense key, return 
  1638.             // an ioErr to the system.
  1639.             returnStatus = ioErr;
  1640.     
  1641.         }
  1642.         else
  1643.         {
  1644.             // No errors has been reported back in the sense key, return 
  1645.             // a noErr to the system.
  1646.             returnStatus = noErr;
  1647.         }
  1648.     }
  1649.     else
  1650.     {
  1651.         // An error occurred on the RequestSense command.  This could
  1652.         // indicate a drive problem.  Return an error and let the UnitTable Module retry.
  1653.         returnStatus = ioErr;
  1654.     }
  1655.     
  1656.     FreeInternalPB( requestPB );
  1657.     (*theCallBack) ( theUserData, returnStatus );
  1658. }
  1659.  
  1660. void ReadWriteRequestSenseOnErrorComplete ( IntDriveRequestPBPtr requestPB )
  1661. {
  1662.     DefaultDAMCompletionProcPtr        theCallBack = (DefaultDAMCompletionProcPtr) requestPB->userCompletionProc;
  1663.     OSStatus                        theStatus = ioErr;
  1664.     UInt32                            theUserData = requestPB->userData;
  1665.  
  1666.     // An error occurred on the original command and the RequestSense was done to
  1667.     // clear the error condition on the drive.
  1668.     // Since the original command completed with an error, return an ioErr.
  1669.     // Code to check the Sense data should be added to tell the UnitTable Module
  1670.     // if the command should be retried.
  1671.     FreeInternalPB( requestPB );
  1672.     (*theCallBack) ( theUserData, theStatus );
  1673. }
  1674.  
  1675. //------------------------------------------------------------------------------
  1676. //    Function:        ReadWriteSingleBuffer
  1677. //
  1678. //    Description:    Low level read/write block on the media with retries.
  1679. //
  1680. //    Input:            drive:            Pointer to physical drive record
  1681. //                        blockAddr:        Starting block address 
  1682. //                        blockCount:        Number of blocks to read/write
  1683. //                        buffer:            Pointer to buffer
  1684. //                        doWrite:            1 = write, 0 = read
  1685. //
  1686. //    Output:            true if successful, false if not
  1687. //-------------------------------------------------------------------------------
  1688. OSStatus ReadWriteSingleBuffer( UInt32 userData, UInt32 startBlock, UInt32 blockCount, Ptr buffer, 
  1689.             UInt32 byteCount, Boolean doWrite, RWBlocksCompletionProcPtr callBack )
  1690. {    
  1691.     OSStatus                    status = noErr;
  1692.     StorageExecuteCommandPB     *theReadWriteRequest;
  1693.     IntDriveRequestPBPtr         ourPB;
  1694.  
  1695.     if ( IsDeviceAccessEnabled() == false )
  1696.     {
  1697.         // The new error code for MacOS 9.0 and later.
  1698.         // It lets the file system know that the device was unplugged.
  1699.         return driverHardwareGoneErr;
  1700.     }
  1701.  
  1702.     if ( WasManualEjectRemoveIssued() == true )
  1703.     {    
  1704.         // The disk was removed, do not try to read or write to it.
  1705.         return ioErr;
  1706.     }
  1707.  
  1708.     ourPB = GetDriveInternalPB();
  1709.     
  1710.     ourPB->userData             = userData;
  1711.     ourPB->userCompletionProc     = callBack;
  1712.     ourPB->isWriteRequest         = doWrite;
  1713.     ourPB->completionProc         = &ReadWriteRequestComplete;
  1714.     
  1715.     theReadWriteRequest = &ourPB->executePB;
  1716.  
  1717.     theReadWriteRequest->userBuffer = buffer;                            // -> Pointer to user buffer
  1718.     theReadWriteRequest->expectedCount = byteCount;                        // -> Expected number of bytes to transfer
  1719.  
  1720.     theReadWriteRequest->completionProc = &DeviceRequestCompletion;        // -> Completion routine
  1721.     theReadWriteRequest->actualCount = 0;                                // <- Actual number of bytes transferred
  1722.     theReadWriteRequest->status = 0;                                    // <- Result of operation
  1723.     
  1724.     if(doWrite)
  1725.     {
  1726.         theReadWriteRequest->cdb[0] = kCmdWrite;
  1727.         theReadWriteRequest->flags     = kStorageDataOut;                    // -> Expect a data out transfer
  1728.         
  1729.         if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
  1730.         {
  1731.             // We have a floppy device, make sure we wait for the interrupt
  1732.             theReadWriteRequest->flags |= kStorageUseCommandCompletionInt;    // -> Wait for the interrupt
  1733.         }
  1734.     }
  1735.     else
  1736.     {
  1737.         theReadWriteRequest->cdb[0] = kCmdRead;
  1738.         theReadWriteRequest->flags     = kStorageDataIn;                    // -> Expect a data in transfer
  1739.     }
  1740.     
  1741.     // Set the starting block in the CDB
  1742.     theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
  1743.     theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
  1744.     theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
  1745.     theReadWriteRequest->cdb[5] = startBlock & 0xff;
  1746.     
  1747.     // Set the Block Count in the CDB
  1748.     theReadWriteRequest->cdb[7] = (blockCount >> 8) & 0xff;
  1749.     theReadWriteRequest->cdb[8] = blockCount & 0xff;
  1750.  
  1751.     status = SendDeviceRequest( theReadWriteRequest );            
  1752.     if (status != kRequestPending )
  1753.     {
  1754.         FreeInternalPB( ourPB );
  1755.     }
  1756.     
  1757.     return status;
  1758. }
  1759.  
  1760. //------------------------------------------------------------------------------
  1761. //    Function:        ReadWriteScatterGatherList
  1762. //
  1763. //    Description:    Low level read/write block on the media with retries.
  1764. //
  1765. //    Input:            drive:            Pointer to physical drive record
  1766. //                        blockAddr:        Starting block address 
  1767. //                        blockCount:        Number of blocks to read/write
  1768. //                        buffer:            Pointer to buffer
  1769. //                        doWrite:            1 = write, 0 = read
  1770. //
  1771. //    Output:            true if successful, false if not
  1772. //-------------------------------------------------------------------------------
  1773. OSStatus ReadWriteScatterGatherList( UInt32 userData, UInt32 startBlock, UInt32 blockCount, SGListPtr sgList,
  1774.             UInt32 byteCount, Boolean doWrite, RWScatterGatherCompletionProcPtr callBack )
  1775. {    
  1776.     OSStatus                    status = noErr;
  1777.     StorageExecuteCommandPB     *theReadWriteRequest;
  1778.     IntDriveRequestPBPtr         ourPB;
  1779.  
  1780.     if ( IsDeviceAccessEnabled() == false )
  1781.     {
  1782.         // The new error code for MacOS 9.0 and later.
  1783.         // It lets the file system know that the device was unplugged.
  1784.         return driverHardwareGoneErr;
  1785.     }
  1786.     
  1787.     if ( WasManualEjectRemoveIssued() == true )
  1788.     {    
  1789.         // The disk was removed, do not try to read or write to it.
  1790.         return ioErr;
  1791.     }
  1792.  
  1793.     ourPB = GetDriveInternalPB();
  1794.     
  1795.     ourPB->userData             = userData;
  1796.     ourPB->userCompletionProc     = callBack;
  1797.     ourPB->isWriteRequest         = doWrite;
  1798.     ourPB->completionProc         = &ReadWriteRequestComplete;
  1799.  
  1800.     theReadWriteRequest = &ourPB->executePB;
  1801.  
  1802.     theReadWriteRequest->userBuffer = (Ptr) sgList;                        // -> Pointer to user buffer
  1803.     theReadWriteRequest->expectedCount = byteCount;                        // -> Expected number of bytes to transfer
  1804.  
  1805.     theReadWriteRequest->completionProc = &DeviceRequestCompletion;        // -> Completion routine
  1806.     theReadWriteRequest->actualCount = 0;                                // <- Actual number of bytes transferred
  1807.     theReadWriteRequest->status = 0;                                    // <- Result of operation
  1808.  
  1809.     if(doWrite)
  1810.     {
  1811.         theReadWriteRequest->cdb[0] = kCmdWrite;
  1812.         theReadWriteRequest->flags     = kStorageDataOut;                            // -> Expect a data out transfer
  1813.         
  1814.         if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
  1815.         {
  1816.             // We have a floppy disk, make sure we wait for the interrupt
  1817.             theReadWriteRequest->flags     |= kStorageUseCommandCompletionInt;        // -> Wait for the interrupt
  1818.         }
  1819.     }
  1820.     else
  1821.     {
  1822.         theReadWriteRequest->cdb[0] = kCmdRead;
  1823.         theReadWriteRequest->flags     = kStorageDataIn;        // -> Expect a data in transfer
  1824.     }
  1825.     
  1826.     theReadWriteRequest->flags     |= kStorageSGBuffer;        // -> Tell class driver that this is a SG list operation
  1827.     
  1828.     // Set the starting block in the CDB
  1829.     theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
  1830.     theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
  1831.     theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
  1832.     theReadWriteRequest->cdb[5] = startBlock & 0xff;
  1833.     
  1834.     // Set the Block Count in the CDB
  1835.     theReadWriteRequest->cdb[7] = (blockCount >> 8) & 0xff;
  1836.     theReadWriteRequest->cdb[8] = blockCount & 0xff;
  1837.  
  1838.     status = SendDeviceRequest( theReadWriteRequest );            
  1839.     if (status != kRequestPending )
  1840.     {
  1841.         FreeInternalPB( ourPB );
  1842.     }
  1843.     
  1844.     return status;
  1845. }
  1846.  
  1847.  
  1848. //------------------------------------------------------------------------------
  1849. //    Function:        FlushDriveWriteCache
  1850. //
  1851. //    Description:    Flushes the drive's write cache
  1852. //                    
  1853. //    Input:            None.
  1854. //
  1855. //    Output:            None.
  1856. //-------------------------------------------------------------------------------
  1857. OSStatus FlushDriveWriteCache( UInt32 userData, FlushCacheCompletionProcPtr callBack )
  1858. {
  1859. #pragma unused( userData, callBack )
  1860.     // Unfortunately, there is no consistent method for flushing the write cache on
  1861.     // all drives.  However, all drives should flush the cache within a couple of
  1862.     // rotations so we can simply hang out for a bit and hope the cache is flushed.
  1863.     // Use a non-interrupt timer since interrupts may be disabled at this time.
  1864.     AbsoluteTime    theWait;
  1865.     
  1866.     theWait = DurationToAbsolute(durationMillisecond*500);
  1867.     DelayForHardware( theWait );            // delay for max seek and a few rotations (500 msec.)
  1868.     
  1869.     return noErr;
  1870. }
  1871.  
  1872.  
  1873. #pragma mark -
  1874. #pragma mark Format Media Support
  1875.  
  1876. enum
  1877. {
  1878.     kFormatRequestSenseRetryCount = 5,
  1879.  
  1880.     // Format State Machine
  1881.     kFormatStartState = 1,
  1882.     kFormatDoneState,
  1883.     kFormatRequestSenseDoneState,
  1884.     kFormatGetGeometryDoneState
  1885. };
  1886.  
  1887. static UInt16    gFormatRetryCount;
  1888.  
  1889. static void FormatMediaComplete ( IntDriveRequestPBPtr ourPB );
  1890.  
  1891. // Floppy drive track by track format state machine
  1892. static OSStatus     FormatFloppyCartridge( IntDriveRequestPBPtr requestPB, UInt32 FormatCapacity, UInt16 blockSize );
  1893.  
  1894. // Expects a buffer of at least 0x0C (12) 
  1895. OSStatus FormatMedia( UInt32 userData, UInt32 FormatCapacity, UInt16 blockSize, FormatMediaCompletionProcPtr callBack )
  1896. {
  1897.     OSStatus                    status = noErr;
  1898.     StorageExecuteCommandPB     *commandPB;
  1899.     IntDriveRequestPBPtr         ourPB;
  1900.  
  1901.     if ( WasManualEjectRemoveIssued() == true )
  1902.     {    
  1903.         // The disk was removed, do not try to format it.
  1904.         return ioErr;
  1905.     }
  1906.  
  1907.     ourPB = GetDriveInternalPB();
  1908.     
  1909.     ourPB->userData             = userData;
  1910.     ourPB->userCompletionProc     = callBack;
  1911.     ourPB->completionProc         = &FormatMediaComplete;
  1912.     ourPB->currentExecutionState =     kFormatDoneState;
  1913.  
  1914.     gFormatRetryCount = 0;
  1915.     
  1916.     if ( gDeviceUSBSubClass == kUSBStorageUFISubclass )
  1917.     {
  1918.         // If this is a floppy subclass device, do a track by track format
  1919.         status = FormatFloppyCartridge( ourPB, FormatCapacity, blockSize );
  1920.         return status;
  1921.     }
  1922.  
  1923.     commandPB = &ourPB->executePB;
  1924.  
  1925.     BlockZero( ourPB->buffer, 0x0C);
  1926.  
  1927.     // Set up the data going out
  1928.     // First set up the Defect List Header
  1929.     ourPB->buffer[0] = 0;
  1930.     ourPB->buffer[1] = 0;
  1931.     ourPB->buffer[2] = 0;
  1932.     ourPB->buffer[3] = 0x08;
  1933.  
  1934.     *((UInt32 *) &ourPB->buffer[4]) = FormatCapacity;
  1935.     *((UInt16 *) &ourPB->buffer[10]) = blockSize;
  1936.     
  1937.     commandPB->cdb[0] =            kCmdFormat;
  1938.     commandPB->cdb[1] =            0x17;
  1939.                 
  1940.     commandPB->flags =             kStorageDataOut;                    // -> Expect a data out transfer
  1941.     commandPB->userBuffer =        (Ptr) ourPB->buffer;                // -> Pointer to user buffer
  1942.     commandPB->expectedCount =    0x0C;                                // -> Expected number of bytes to transfer
  1943.         
  1944.     commandPB->completionProc = &DeviceRequestCompletion;                // -> Completion routine
  1945.  
  1946.     status = SendDeviceRequest(commandPB);
  1947.     if (status != kRequestPending )
  1948.     {
  1949.         FreeInternalPB( ourPB );
  1950.     }
  1951.     
  1952.     return status;
  1953. }
  1954.  
  1955. void FormatMediaComplete ( IntDriveRequestPBPtr ourPB )
  1956. {
  1957.     OSStatus    err = ourPB->status;
  1958.     
  1959.     switch (ourPB->currentExecutionState)
  1960.     {
  1961.         case kFormatDoneState:
  1962.         {
  1963.             ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1964.             err =  RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
  1965.         }
  1966.         break;
  1967.         
  1968.         case kFormatRequestSenseDoneState:
  1969.         {
  1970.             FixRequestSenseData( ourPB );
  1971.             err = ourPB->status;
  1972.             if(err == noErr)
  1973.             {
  1974.                 if(ourPB->buffer[12] == 0x00)
  1975.                 {
  1976.                     ourPB->currentExecutionState = kFormatGetGeometryDoneState;
  1977.                     ourPB->completionProc = &FormatMediaComplete;
  1978.                     err = GetMediaGeometryBuilder( ourPB, &gCurrentMediaCapacity );
  1979.                 }
  1980.                 else
  1981.                 {
  1982.                     // The format has failed, inform the OS
  1983.                     err = ioErr;
  1984.                 }
  1985.             }
  1986.             else
  1987.             {
  1988.                 if (gFormatRetryCount < kFormatRequestSenseRetryCount)
  1989.                 {
  1990.                     ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1991.                     gFormatRetryCount += 1;
  1992.                     ourPB->completionProc = &FormatMediaComplete;
  1993.                     err = RequestSenseBuilder( ourPB, (Ptr) ourPB->buffer );
  1994.                 }
  1995.                 else
  1996.                 {
  1997.                     // The format has failed, inform the OS
  1998.                     gFormatRetryCount = 0;
  1999.                     err = ioErr;
  2000.                 }
  2001.             }
  2002.         }
  2003.         break;
  2004.  
  2005.         case kFormatGetGeometryDoneState:
  2006.         {
  2007.             if ( err != noErr )
  2008.             {
  2009.                 err = ioErr;
  2010.                 gCurrentMediaCapacity.TotalBlocksOnMedia = 0;
  2011.                 gCurrentMediaCapacity.BlockLengthInBytes = 0;
  2012.             }
  2013.             else
  2014.             {
  2015.                 if ( gCurrentMediaCapacity.TotalBlocksOnMedia != 0)
  2016.                 {
  2017.                     // Since the Read Capacity command returns the last 
  2018.                     // block number, add one to make the actual total.
  2019.                     gCurrentMediaCapacity.TotalBlocksOnMedia +=1; 
  2020.                 }
  2021.             }
  2022.         }
  2023.         break;
  2024.     }
  2025.  
  2026.     if ( err != kRequestPending )
  2027.     {
  2028.         UInt32                                 theUserData;
  2029.         FormatMediaCompletionProcPtr        theCallBack;
  2030.         
  2031.         theUserData = ourPB->userData;
  2032.         theCallBack = (FormatMediaCompletionProcPtr) ourPB->userCompletionProc;
  2033.         FreeInternalPB( ourPB );
  2034.         (*theCallBack) ( theUserData, err);
  2035.     }
  2036. }
  2037.  
  2038. #pragma mark -
  2039.  
  2040. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  2041. // Floppy Format Machine
  2042. // All functions and variables needed to handle the formatting
  2043. // of the media in a floppy only drive.
  2044. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  2045. enum
  2046. {
  2047.     kFormatFloppyStartState = 1,
  2048.     kFormatFloppyFormatTrackSide0,
  2049.     kFormatFloppyFormatTrackSide1
  2050. };
  2051.  
  2052. static void FormatFloppyStateMachine( IntDriveRequestPBPtr ourPB );
  2053.  
  2054. // This is the current track counter
  2055. static UInt16 gCurrentFloppyFormatTrack = 0;
  2056. static UInt32 gFloppyFormatCapacityParam;
  2057. static UInt32 gFloppyFormatBlockSizeParam;
  2058.  
  2059. OSStatus FormatFloppyCartridge( IntDriveRequestPBPtr requestPB, UInt32 FormatCapacity, UInt16 blockSize )
  2060. {
  2061.     if ( IsDeviceAccessEnabled() == false )
  2062.     {
  2063.         return kDeviceAccessNoMediaError;
  2064.     }
  2065.     
  2066.     gCurrentFloppyFormatTrack = 0;
  2067.     gFloppyFormatCapacityParam = FormatCapacity;
  2068.     gFloppyFormatBlockSizeParam = blockSize;
  2069.  
  2070.     requestPB->currentExecutionState = kFormatFloppyStartState;
  2071.     requestPB->completionProc = &FormatFloppyStateMachine;
  2072.     requestPB->status = kRequestPending;
  2073.     FormatFloppyStateMachine( requestPB );
  2074.     
  2075.     return kRequestPending;
  2076. }
  2077.  
  2078. void FormatFloppyStateMachine( IntDriveRequestPBPtr ourPB )
  2079. {
  2080.     OSStatus            err = ourPB->status;
  2081.     
  2082.     switch (ourPB->currentExecutionState)
  2083.     {
  2084.         case kFormatFloppyStartState:
  2085.         {
  2086.             // Format current track on side 0
  2087.             ourPB->currentExecutionState = kFormatFloppyFormatTrackSide1;
  2088.             ourPB->completionProc = &FormatFloppyStateMachine;
  2089.             err = FormatFloppyTrackBuilder( ourPB, ourPB->buffer, gFloppyFormatCapacityParam, gFloppyFormatBlockSizeParam, gCurrentFloppyFormatTrack, 0);
  2090.         }
  2091.         break;
  2092.  
  2093.         case kFormatFloppyFormatTrackSide0:
  2094.         {
  2095.             // Check if an error occurred
  2096.             if ( err != noErr )
  2097.             {
  2098.                 break;
  2099.             }
  2100.             
  2101.             // Increment the track counter
  2102.             gCurrentFloppyFormatTrack++;
  2103.  
  2104.             // Check if all tracks are done            
  2105.             if ( gCurrentFloppyFormatTrack >= 0x50)
  2106.             {
  2107.                 // We have written all tracks on both sides
  2108.                 // we are done, signal to do a client callback
  2109.                 err = noErr;
  2110.                 break;
  2111.             }
  2112.             
  2113.             // Format current track on side 1
  2114.             ourPB->currentExecutionState = kFormatFloppyFormatTrackSide1;
  2115.             ourPB->completionProc = &FormatFloppyStateMachine;
  2116.             err = FormatFloppyTrackBuilder( ourPB, ourPB->buffer, gFloppyFormatCapacityParam, gFloppyFormatBlockSizeParam, gCurrentFloppyFormatTrack, 0);
  2117.         }
  2118.         break;
  2119.  
  2120.         case kFormatFloppyFormatTrackSide1:
  2121.         {
  2122.             // Check if an error occurred
  2123.             if ( err != noErr )
  2124.             {
  2125.                 break;
  2126.             }
  2127.             
  2128.             // Format current track on side 1
  2129.             ourPB->currentExecutionState = kFormatFloppyFormatTrackSide0;
  2130.             ourPB->completionProc = &FormatFloppyStateMachine;
  2131.             err = FormatFloppyTrackBuilder( ourPB, ourPB->buffer, gFloppyFormatCapacityParam, gFloppyFormatBlockSizeParam, gCurrentFloppyFormatTrack, 1);
  2132.         }
  2133.         break;
  2134.     }
  2135.     
  2136.     if ( err != kRequestPending )
  2137.     {
  2138.         if ( err == noErr )
  2139.         {
  2140.             // No errors occurred, on to the next part of 
  2141.             // the process.
  2142.             ourPB->completionProc         = &FormatMediaComplete;
  2143.             ourPB->currentExecutionState =     kFormatDoneState;
  2144.             ourPB->status = kRequestPending;
  2145.             FormatMediaComplete ( ourPB );
  2146.         }
  2147.         else
  2148.         {
  2149.             // An error occurred, return error back to client.
  2150.             UInt32                             theUserData;
  2151.             DefaultDAMCompletionProcPtr        theCallBack;
  2152.             
  2153.             theUserData = ourPB->userData;
  2154.             theCallBack = (DefaultDAMCompletionProcPtr) ourPB->userCompletionProc;
  2155.             FreeInternalPB( ourPB );
  2156.             (*theCallBack) ( theUserData, err);
  2157.         }
  2158.     }
  2159. }
  2160.  
  2161.  
  2162.